use crate::model::{Comment, PartialComment};
use crate::utils;
use crate::Request;

// use sqlx::prelude::*;
use chrono::prelude::*;
use md5;
use serde::{Deserialize, Serialize};
use tide::http::mime;
use tide::prelude::json;
use tide::{Response, StatusCode};
use validator::Validate;

#[derive(Debug, Deserialize, Serialize, Validate)]
#[serde(default)]
struct QueryPage {
    size: u8,
    offset: u8,
}

impl Default for QueryPage {
    fn default() -> Self {
        Self {
            offset: 0,
            size: 10,
        }
    }
}

#[derive(Debug, Deserialize, Serialize, Validate)]
#[serde(default)]
struct QueryIndex {
    size: u8,
    offset: u8,

    #[validate(url(message = "需要链接地址格式"))]
    url: Option<String>,
    hash: Option<String>,
}

impl Default for QueryIndex {
    fn default() -> Self {
        Self {
            offset: 0,
            size: 10,
            url: Some("".to_string()),
            hash: Some("".to_string()),
        }
    }
}

pub async fn index(request: Request) -> tide::Result {
    let mut query: QueryIndex = request.query()?;

    if let Err(e) = query.validate() {
        let re = Response::builder(StatusCode::Ok)
            .content_type(mime::JSON)
            .body(json!({ "code": 0 , "error": e, "query": query}))
            .build();

        return Ok(re);
    }

    let digest = md5::compute(query.url.clone().unwrap().into_bytes());
    query.hash = Some(String::from(format!("{:x}", digest)));

    let pool = request.state().db.clone();
    let list: Vec<Comment> = Comment::url_list(
        &pool,
        query.hash.unwrap(),
        query.offset as i8,
        query.size as i8,
    )
    .await?;

    let re = Response::builder(StatusCode::Ok)
        .content_type(mime::JSON)
        // .body(Body::from_json(&list)?)
        .body(json!({ "code":1, "size":query.size, "offset": query.offset, "list":&list}))
        .build();

    Ok(re)
}

pub async fn show(request: Request) -> tide::Result {
    let pool = request.state().db.clone();
    let one: Comment = Comment::find_by_id(&pool, request.param("id")?).await?;

    let now = Local::now();

    let re = Response::builder(StatusCode::Ok)
        .content_type(mime::JSON)
        .body(json!({
            "code":1,
            "now local": now,
            "body": &one,
        }))
        .build();
    Ok(re)
}

pub async fn delete(request: Request) -> tide::Result {
    let pool = request.state().db.clone();
    Comment::delete_by_id(&pool, request.param("id")?).await?;

    let re = Response::builder(StatusCode::Ok)
        .content_type(mime::JSON)
        .body(json!({"code":1}))
        .build();
    Ok(re)
}

pub async fn update(mut request: Request) -> tide::Result {
    let one: PartialComment = utils::deserialize_body(&mut request).await?;
    let id = request.param("id")?;
    let pool = request.state().db.clone();

    let rows_updated = one.update_by_id(&pool, id).await?;

    let body = if rows_updated {
        json!({"code":1})
    } else {
        json!({"code":0})
    };

    let re = Response::builder(StatusCode::Ok)
        .content_type(mime::JSON)
        .body(body)
        .build();

    Ok(re)
}

pub async fn create(mut request: Request) -> tide::Result {
    let pool = request.state().db.clone();
    // let mut tx = db.begin().await?;
    // let article: PartialComment = utils::deserialize_body(&mut request).await?;
    let mut one: PartialComment = request.body_json().await?;

    // match one.validate() {
    //     Ok(_) => (),
    //     Err(e) => return e;
    // };
    if let Err(e) = one.validate() {
        let re = Response::builder(StatusCode::Ok)
            .content_type(mime::JSON)
            .body(json!({ "code": 0 , "error": e }))
            .build();

        return Ok(re);
    }

    let digest = md5::compute(one.url.clone().unwrap().into_bytes());
    one.hash = Some(String::from(format!("{:x}", digest)));

    let created_id = one.create(&pool).await?;

    let re = Response::builder(StatusCode::Ok)
        .content_type(mime::JSON)
        .body(json!({"code":1, "id": created_id }))
        .build();

    Ok(re)
}
